package com.wlyuan.gateway.utils;

import com.alibaba.fastjson.JSON;
import com.wlyuan.utils.Result;
import com.wlyuan.utils.ResultError;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @author yuanzheng
 */
@Slf4j
public class ResponseUtils {
    private static final Integer UNAUTHORIZED = 401;
    private static final Integer FORBIDDEN = 403;

    private static final List<HttpMethod> ALLOW_METHODS = Arrays.asList(HttpMethod.OPTIONS, HttpMethod.GET, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.POST, HttpMethod.PATCH);

    private static void addHeaders(HttpHeaders headers) {
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        headers.setAccessControlAllowMethods(ALLOW_METHODS);
        headers.setAccessControlAllowOrigin("*");
        headers.setAccessControlAllowHeaders(Collections.singletonList("*"));
        headers.setAccessControlAllowCredentials(true);
        headers.setAccessControlMaxAge(3600);
    }

    public static Mono<Void> writeWith(ServerHttpResponse response, ResultError error) {
        logger.error("Response with error: {}", JSON.toJSONString(error));
        addHeaders(response.getHeaders());
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(error).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }

    public static Mono<Void> writeWith(ServerHttpResponse response, AuthenticationException e) {
        addHeaders(response.getHeaders());
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        DataBuffer buffer;
        if (e.getCause() instanceof InvalidTokenException) {
            logger.error("Invalid token", e);
            var error = Result.error(UNAUTHORIZED, "登录已失效");
            buffer = response.bufferFactory().wrap(JSON.toJSONString(error).getBytes(StandardCharsets.UTF_8));
        } else {
            logger.error("Unauthorized", e);
            var error = Result.error(UNAUTHORIZED, "用户未登录");
            buffer = response.bufferFactory().wrap(JSON.toJSONString(error).getBytes(StandardCharsets.UTF_8));
        }
        return response.writeWith(Mono.just(buffer));
    }

    public static Mono<Void> writeWith(ServerHttpResponse response, AccessDeniedException e) {
        logger.error("Access denied", e);
        addHeaders(response.getHeaders());
        response.setStatusCode(HttpStatus.FORBIDDEN);
        var error = Result.error(FORBIDDEN, e.getMessage());
        DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONString(error).getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }
}
